from Fuzzer import Fuzzer
from Ranker import Ranker
from ChainBuilder import ChainBuilder
from time import time

class BugRanker:
    
    def __init__ (self, methodUnderTest, oracle):
        self.__method = methodUnderTest
        self.__oracle = oracle
        self.__fuzzer = Fuzzer()
        self.__ranker = Ranker()
        self.__generator = ChainBuilder(methodUnderTest, oracle)
        self.__chainCounter = 0
        self.__reports = {False:None,True:None}
        self.__ranking = None
        
    def setFuzzer(self, fuzzer=None):
        if fuzzer is None:
            self.__fuzzer = Fuzzer()
        else:
            self.__fuzzer = fuzzer
        
    def generateChains(self, numberOfChains):
        assert numberOfChains > 0, 'Number of chains must be greater than zero! (Given %d)' % numberOfChains
        print 'Generating Cause-Effect Chains.'
        overallTime = 0
        for i in range(numberOfChains):
            start = time()
            # generate input for the builder
            failing, passing, failCov, passCov = self.__fuzzer.getNextPair(self.__method, self.__oracle)
            # print process information
            if not failing:
                print '\tFailing run could not be found'
                continue
            if not passing:
                print '\tPassing run could not be found'
                continue
            c = self.__generator.getCEChain(failing, passing, failCov, passCov)
            if c.getCauseEffectChain():
                self.__ranker.addCEChain(c)
                self.__chainCounter += 1
            else: 
                print '\tCould not build minimal chain!'
            duration = time() - start
            print "Chain {}/{} {:.2f} sec.".format(i + 1, numberOfChains, duration)
            overallTime += duration
        print 'Generated {} chain{} in {:.2f} seconds.\n'.format(numberOfChains,''if numberOfChains==1 else's',overallTime)
        
    def printBugList(self, parallelView=False):
        print self.createReport(parallelView)
            
    def createReport(self, parallelView):
        if self.__reports[parallelView] is not None:
            return self.__reports[parallelView]
        if self.__ranking is None:
            self.__ranking = self.__ranker.getOrderedBugList()
        bugs = len(self.__ranking)
        s = ''
        if bugs==0:
            s += 'No bugs could be found after examining %d cause-effect chains' % self.__chainCounter
        elif bugs==1:
            s += 'One bug was found after examining %d cause-effect chains:\n' % self.__chainCounter
        else:
            s += 'Following {} bugs were found after examining {} cause-effect chains:\n'.format(bugs,self.__chainCounter)
        bugNum = 0
        for bug in self.__ranking:
            bugNum += 1
            s+= 'Bug No.%d:\n'%bugNum
            s+= 'Encounters: %d\n'%bug.size()
            s+= 'Relative frequency: {:.2%}\n'.format(float(bug.size())/self.__chainCounter)
            s+= 'Shortest chain:\n'
            s+= bug.getMinCauseChain().toStringParallel(1) if parallelView else bug.getMinCauseChain().toString(1)
        self.__reports[parallelView] = s
        return s
    
    def dumpBugList(self, filename, parallelView=False):
        assert filename
        with open(filename, 'w') as f:
            f.write(self.createReport(parallelView))
